package game.objects.npc;
import java.util.Vector;

import physics.Body;
import physics.PhysicalConstants;
import physics.collision.RayCastInfo;
import physics.joints.JBackgroundAngleJoint;
import editors.objectedit.BoundingBox;
import editors.objectedit.LoadObject;
import game.Game;
import game.ai.fsm.Condition;
import game.ai.fsm.ConditionHandler;
import game.ai.fsm.State;
import game.ai.fsm.StateHandler;
import game.ai.fsm.Transition;
import game.controlling.Controller;
import game.engine.Empty;
import game.engine.GSync;
import game.objects.GOPlayer;
import game.objects.GORocket;
import game.objects.GameObject;
import game.objects.interfaces.Damageable;
import game.objects.interfaces.Ownable;
import game.waypoint.Path;
import graphics.effects.GEFire;
import graphics.effects.GESmoke;
import graphics.objects.GCircle;
import graphics.objects.GLine;
import graphics.objects.GShape;
import sound.main.Sound;
import sound.main.SoundManager;
import tools.Floatmath;
import tools.Help;
import tools.Log;
import tools.Point;
import tools.id.IdObject;
import graphics.objects.GPolygon;
import graphics.system.Color;
public class GODrone extends FSMNPC implements Damageable {
	private static final float SHOTINTERVAL = 3;
	public static final float MOVESPEED = 0.9f;
	public static final float MAXSPEED = 5f;
	private GShape led;
	private Body body;
	float blink;
	private Path path;
	private float lastshottime;
	private Point aim;
	private GEFire fire;
	private Sound boostsound;
	private Sound beepsound;
	private Sound idleSound;
	private float aimtime;
	private JBackgroundAngleJoint jangle;
	public Point fireat;
	float hp = 1;
	private boolean dead;
	private GESmoke smoke;
	public GODrone(Game g, Point pos) {
		super(g, pos);
		initSound();
		new State(this, "idle", 100, new Idle());
		new State(this, "shoot", 100, new Shoot());
		new State(this, "findaim", -1);
		new State(this, "move", -1, new Move());
		new Condition(this, "playerfound", new Playerfinder());
		new Condition(this, "aimfound", new Aimfinder());
		new Condition(this, "aimreached", false);
		new Transition(this, "idle", "shoot", "playerfound");
		new Transition(this, "findaim", "move", "aimfound");
		new Transition(this, "shoot", "idle");
		new Transition(this, "idle", "findaim");
		new Transition(this, "move", "idle", "aimreached");
		new Transition(this, "move", "shoot", "playerfound");
		// new Transition(this,"move","idle");
		this.currentState = getState("idle").switchTo();
	}
	public void initSound() {
		boostsound = new Sound("boost.wav");
		boostsound.setLoop(true);
		boostsound.setVolume(0.3f);
		beepsound = new Sound("beep.wav");
		beepsound.setVolume(0.5f);
		
		idleSound = new Sound("dronebla.ogg");
		
	}
	@Override
	public void handle() {
		if (!game.server)
			return;
		if (!dead) {
			handleBlinking();
			keepFloating();
			super.handle();
			makeSounds();
		}
		else {
			smoke.add(new Point(0, 50), 1);
		}
	}
	private void handleBlinking() {
		if (led != null) {
			led.glowColor.r = 0.2f * Floatmath.sin(game.time * 5) + 0.8f;
			if (game.time - blink > 2) {
				float t = (game.time - blink) - 2;
				led.node.scale.y = Floatmath.min(0.4f, Math.abs(0.4f - t * 5));
				if (game.time - blink > 2.25)
					blink = game.time;
			}
		}
	}
	private void makeSounds() {
		if (Floatmath.random() < 0.001) {
			idleSound.setPos(body.pos);
			idleSound.replay();
		}
	}
	private void keepFloating() {
		body.applyForce(new Point(0, -PhysicalConstants.GRAVITY * PhysicalConstants.UNITSPERMETER * body.mass * 2));
	}
	private void newAim() {
		if (path == null) {
			Vector<Point> ppos = this.getPlayerPositions(body.pos, 4800);
			if (ppos.size() > 0) {
				// Help.p("searching path!");
				path = game.getMap().getPathTo(body.pos, ppos.get(Floatmath.randInt(0, ppos.size())), false);
				if (path.length() > 0) {
					aim = path.getCurrentAim();
					game.engine.debugpath = path;
					aimtime = game.time;
				}
				else {
					path = null;
					game.engine.debugpath = null;
				}
			}
		}
		if (path == null) { // no path found -> move randomly
			path = game.getMap().getPathTo(body.pos, body.pos.add(Point.randPoint(1000)), false);
			if (path.length() > 0) {
				aim = path.getCurrentAim();
				game.engine.debugpath = path;
				aimtime = game.time;
			}
			else {
				path = null;
				game.engine.debugpath = null;
				aim = body.pos.add(Point.randPoint(1000));
				aimtime = game.time;
			}
		}
	}
	private void fireRocket(Point pos) {
		if (game.time - lastshottime > SHOTINTERVAL && Floatmath.random() > 0.9) {
			Point dir = pos.sub(body.pos).normalize();
			GORocket rocket = new GORocket(game, body.pos.add(dir.mul(70)), dir.angle());
			game.addGameObject(rocket);
			lastshottime = game.time;
		}
	}
	@Override
	public void initialize(Point pos) {
		LoadObject.load("drone", this, new BoundingBox(pos.x - 50, pos.y - 50, pos.x + 50, pos.y + 50));
		led = (GShape) this.getGraphicsObjectByName("eye");
		blink = game.time + Floatmath.random() * 2;
		body = this.getBodyByName("body");
		jangle = new JBackgroundAngleJoint(body, 0);
		jangle.setStrength(0.0003f);
		addJoint(jangle);
		// aline = new GLine(led.node.pos, aim,0.1f,new Color(1,0.3f,0.1f,0.5f));
		// aline.setShadow(false);
		fire = new GEFire(this.getEmptyByName("fire").getPos());
		addGraphicsObject(fire, new GSync(body, this.getEmptyByName("fire").getPos().sub(body.pos), 0));
		smoke = new GESmoke(body.pos);
		addGraphicsObject(fire, new GSync(body));
	}
	public void stop() {
		body.applyImpulse(body.vel.mul(-0.9f));
		path = null;
	}
	private class Playerfinder implements ConditionHandler {
		@Override
		public boolean isFulfilled() {
			// Help.p("searching player");
			Vector<Point> ppos = getVisiblePlayerPositions(body.pos, 40, 3800);
			if (ppos.size() > 0) {
				fireat = ppos.get(Floatmath.randInt(0, ppos.size()));
				return true;
			}
			return false;
		}
	}
	private class Aimfinder implements ConditionHandler {
		@Override
		public boolean isFulfilled() {
			newAim();
			return true;
		}
	}
	private class Shoot implements StateHandler {
		public void handle() {
			fireRocket(fireat);
			stop();
		}
	}
	private class Move implements StateHandler {
		public void handle() {
			if (game.time - aimtime > 10) {
				getCondition("aimreached").setFulfilled();
				return;
			}
			if (path != null) {
				Point dx = aim.sub(body.pos);
				float l = dx.length();
				if (l < 100) {
					if (path.atLastPoint()) {
						getCondition("aimreached").setFulfilled();
						return;
					}
					aim = path.getNextPoint();
				}
				body.applyImpulse(dx.mul(1 / l).mul(Floatmath.max(MOVESPEED * l, MAXSPEED)));
				body.applyImpulse(body.vel.mul(-0.5f));
			}
			else {
				Point dx = aim.sub(body.pos);
				float l = dx.length();
				if (l < 100) {
					getCondition("aimreached").setFulfilled();
					return;
				}
				body.applyImpulse(dx.mul(1 / l).mul(Floatmath.max(MOVESPEED * l, MAXSPEED)));
				body.applyImpulse(body.vel.mul(-0.5f));
			}
		}
	}
	private class Idle implements StateHandler {
		public void handle() {
			stop();
		}
	}
	@Override
	public void destroy() {
		dead = true;
		remJoint(jangle);
	}
}
